/*
  S.M.A.C.K - An operating system kernel
  Copyright (C) 2010,2011 Mattias Holm and Kristian Rietveld
  For licensing and a full list of authors of the kernel, see the files
  COPYING and AUTHORS.
*/

/*!
  \defgroup VM
  This module contains all the VM related functions.

  \addtogroup VM
  \{
*/

#ifndef KERNEL__VM_H
#define KERNEL__VM_H

#include <stdbool.h>
#include <stddef.h>
#include <list.h>
#include <arch-types.h>

#define VM_SUPER_READ  (1 << 0)
#define VM_SUPER_WRITE (1 << 1)
#define VM_SUPER_EXEC  (1 << 2)

#define VM_SUPER_RW    (VM_SUPER_READ|VM_SUPER_WRITE)
#define VM_SUPER_RX    (VM_SUPER_READ|VM_SUPER_EXEC)
#define VM_SUPER_RWX   (VM_SUPER_READ|VM_SUPER_WRITE|VM_SUPER_EXEC)

#define VM_USER_READ   (1 << 3)
#define VM_USER_WRITE  (1 << 4)
#define VM_USER_EXEC   (1 << 5)

#define VM_EXEC        (VM_SUPER_EXEC|VM_USER_EXEC)

#define VM_USER_RW     (VM_USER_READ|VM_USER_WRITE)
#define VM_USER_RX     (VM_USER_READ|VM_USER_EXEC)
#define VM_USER_RWX    (VM_USER_READ|VM_USER_WRITE|VM_USER_EXEC)

#define VM_RW          (VM_USER_RW|VM_SUPER_RW)
#define VM_RO          (VM_USER_RO|VM_SUPER_RO)
#define VM_RWX         (VM_USER_RWX|VM_SUPER_RWX)

#define VM_PRIV_MASK   (VM_USER_RWX | VM_SUPER_RWX)

#define VM_NULL        (1 << 6)

#define VM_DEVICE      (1 << 7)
#define VM_SHARED      (1 << 8)

#define VM_WIRED       (1 << 9)
#define VM_CONTIG      (1 << 10)
#define VM_NO_CACHE    (1 << 11)

#define KiB *1024
#define MiB *1024*1024
#define GiB *1024*1024


// Standard VM regions
typedef enum {
  VM_REG_NULL,
  VM_REG_USER,
  VM_REG_KERNEL,
  VM_REG_KERNEL_HEAP,
  VM_REG_DEVICE,
} kernel_region_type_t;

typedef enum {
  PA_ROM,
  PA_FLASH,
  PA_RAM,
  PA_IO,
} pa_kind_t;

typedef struct {
  const char *name;
  pa_kind_t kind;
  const pa_t start;
  const size_t size;
} pa_range_t;

typedef struct pmap_region_t {
  LIST_ENTRY(pmap_region_t) link;
  uint32_t flags;
  uint8_t ref_count;
  uint8_t logsize;
} pmap_region_t;

typedef LIST_HEAD(pmap_region_t) pmap_list_t;

typedef struct _vm_region_t {
  LIST_ENTRY(_vm_region_t) link;
  va_t start;
  size_t size;
  uint32_t flags;
  pmap_list_t pmap;
} vm_region_t;

typedef struct _vm_map_t {
  LIST_HEAD(_vm_region_t) region;
  va_t start;
  size_t size;
  void *page_table;
  pa_t page_table_pa;
} vm_map_t;

void vm_init(void);

vm_map_t* vm_get_kernel_map(kernel_region_type_t vm);


/*!
  \function vm_map
  Maps in a segment of RAM.

  The function tries to aquire the virtual address addr (if valid) and the pages
  included within len bytes. The function cannot map in the NULL page by default
  as 0 is used for indicating a don't care address. In order to map in the NULL
  page, which is NOT RECOMMENDED, VM_NULL must be passed in the flags parameter.

  The function will fail if:
    1. Flags is not valid.
    2. Addr is not page aligned
    3. Add is not 0 and, some part of [addr, addr+len] is already mapped.
  \param proc  Process that owns the pages. Pass NULL for kernel memory.
  \param flags Permissions for the memory region.
  \param addr  Requested address, set to 0 if don't care.
  \param len   Length of memory region in bytes.
*/
void* vm_map           (vm_map_t *vm, unsigned flags, va_t addr, size_t len);
void* vm_map_align     (vm_map_t *vm, unsigned flags, va_t alignment, size_t len);

/*!
 \function vm_unmap
 Unmaps the given virtual memory segment.

 The function will unmap the vm segment given addr.

 Vm_unmap will fail if
   1. [Addr, addr+len) is not mapped
   2. [Addr, addr+len) contains pages with different permissions.
*/
void vm_unmap          (vm_map_t *vm, void *addr);


/*!
  \function vm_map_physical
  Creates a mapping for the device at the given address. This is useful for
  assigning virtual addresses to memory mapped devices.
  \param flags Permissions of the memory region.
  \param paddr Physical address of memory mapped registers.
  \param len   Lenght of address range in bytes.
*/
void* vm_map_physical  (vm_map_t *vm, unsigned flags, pa_t paddr, size_t len);

/*!
  \function vm_probe_physical
  Translates virtual address to physical
  \param vaddr Virtual address to translate.
  \result The physical address corresponding to vaddr. If no mapping exists 0 is returned.
*/
pa_t vm_probe_physical (vm_map_t *vm, void *vaddr);

/*!
  \function vm_map_exists
  Returns true if the virtual address has a valid physical address.
*/
bool vm_map_exists     (vm_map_t *vm, void *vaddr);

/*!
  \function vm_valid_user_address_range
  Validates a virtual address range.

  \param start Start address of object
  \param size Size of object in bytes
  \result Returns true if the start and len sized object has a valid vm-region
  mapped in for the current process.
*/
bool vm_valid_user_address_range(void *start, size_t size);


// End doxygen group VM
/*! \} */


#endif /* !KERNEL__VM_H */
